from datetime import datetime
import json
import os
import socket
import struct
import threading
from time import time
from tkinter import messagebox, ttk
import tkinter as tk
import sys

# base_dir = os.path.dirname(os.path.abspath(__file__))
# sys.path.append(os.path.join(base_dir, "..\\gui_util"))
import gui_util.gui_common as gui_common


USER_LOGIN_REQ = 0x1000
USER_LOGIN_RES = 0x1001
USER_LOGOUT_REQ = 0x1002
USER_LOGOUT_RES = 0x1003
USER_LIST_REQ = 0x1004
USER_LIST_RES = 0x1005
USER_CHAT_MSG = 0x1006
USER_CHAT_RES = 0x1007
USER_ONLINE_OFFLINE = 0x1008
USER_REGISTER_REQ = 0x1009
USER_REGISTER_RES = 0x100A
ADD_DEL_FRIEND_REQ = 0x100B
ADD_DEL_FRIEND_RES = 0x100C


def create_msg(msg, id):
    bytes_msg = msg.encode("utf-8")
    bodylen = socket.htonl(len(bytes_msg))
    id = socket.htonl(id)
    return struct.pack("II", id, bodylen) + bytes_msg


def create_user_login_req(user_id, pwd):
    js_string = json.dumps({"id": user_id, "password": pwd}, ensure_ascii=False)
    return create_msg(js_string, USER_LOGIN_REQ)


def handle_user_login_res(data):
    js_string = json.loads(data)
    return js_string


class friend_info:
    def __init__(self, user_id, user_name, online, info_tree_item_id):
        self.user_id = user_id
        self.user_name = user_name
        self.online = online
        self.info_tree_item_id = info_tree_item_id
        self.tool_tip = None


class UserChatMsg:
    def __init__(self, user_id, session_id, to_id, msg):
        self.user_id = user_id
        self.session_id = session_id
        self.to_id = to_id
        self.msg = msg


class UserSession:
    def __init__(self):
        self.user_id = None
        self.user_name = None
        self.pwd = None
        self.sock = None
        self.is_connected = False
        self.is_logged_in = False
        self.stop_event = threading.Event()
        self.info_tree: ttk.Treeview = None
        self.info_tree_item_id = None
        self.recv_thread = None
        self.recv_text_box = None
        self.session_id = None
        self.friend_list: dict[str, friend_info] = {}
        self.friend_id_list: dict[int, friend_info] = {}
        self.tool_tip: str = ""
        self.connection_string: str = ""
        self.root_window = None
        self.login_window = None
        self.all_user_list_event = threading.Event()
        self.all_users: dict[int, friend_info] = {}
        self.all_itemid_user: dict[str, friend_info] = {}

    def get_item_tool_tip(self, item_id):
        if item_id in self.friend_list:
            return self.friend_list[item_id].tool_tip
        else:
            if item_id == self.info_tree_item_id:
                return self.tool_tip
            else:
                self.tool_tip

    def configure_tags(self, tree: ttk.Treeview):
        # 定义并配置不同的标签样式
        tree.tag_configure("connected", background="lightgreen", foreground="black")
        tree.tag_configure("not_connected", background="salmon", foreground="black")
        tree.tag_configure("loggedin", background="lightblue", foreground="black")
        tree.tag_configure("online", background="skyblue", foreground="black")
        tree.tag_configure("offline", background="lightgrey", foreground="black")

    def send_chat_msg(self, item_id, content):
        if item_id not in self.friend_list:
            messagebox.showerror("Error", "You can only send messages to friends")
            return
        friend_user = self.friend_list[item_id]
        js_string = json.dumps(
            {
                "id": self.user_id,
                "session_id": int(self.session_id),
                "to_id": int(friend_user.user_id),
                "msg": content,
            },
            ensure_ascii=False,
        )
        msg = create_msg(js_string, USER_CHAT_MSG)
        self.sock.sendall(msg)
        self.sent_msg = content

    def set_id_pwd(self, user_id, pwd):
        self.user_id = user_id
        self.pwd = pwd

    def set_info_tree(self, tree, text_box):
        self.info_tree = tree
        self.recv_text_box = text_box
        self.configure_tags(self.info_tree)

    def clear_treeview(self):
        # 使用get_children方法获取所有子项
        children = self.info_tree.get_children()
        # 遍历所有子项并删除
        for child in children:
            self.info_tree.delete(child)

    def check_item_onoffline(self, item_id):
        if item_id not in self.friend_list:
            return
        user = self.friend_list[item_id]
        if user.online == False:
            self.info_tree.item(item_id, tags=("offline",))
        else:
            self.info_tree.item(item_id, tags=("online",))

    def register_new_user(self, user_name, pwd):
        if not self.is_connected:
            messagebox.showerror("Error", "You must connect to server first")
            return
        js_string = json.dumps({"name": user_name, "password": pwd}, ensure_ascii=False)
        msg = create_msg(js_string, USER_REGISTER_REQ)
        try:
            self.pwd = pwd
            self.sock.sendall(msg)
        except Exception as e:
            messagebox.showerror("Error", "Error: " + str(e))
            return

    def handle_register_res(self, data):
        js_string = json.loads(data)
        if js_string["result"] == "success":
            self.user_id = js_string["id"]
            self.session_id = js_string["session_id"]
            self.user_name = js_string["name"]
            messagebox.showinfo(
                "Success", "Register success 请牢记你的用户ID :" + str(self.user_id)
            )
            self.login()
        else:
            messagebox.showerror("Error", "Register failed " + js_string["result"])

    def handle_add_friend_req(self, friend_id, add=True):
        if not self.is_connected:
            messagebox.showerror("Error", "You must connect to server first")
            return
        if not self.is_logged_in:
            messagebox.showerror("Error", "You must login first")
            return
        js_string = json.dumps(
            {
                "id": int(self.user_id),
                "session_id": int(self.session_id),
                "friend_id": int(friend_id),
                "add": add,
            },
            ensure_ascii=False,
        )
        msg = create_msg(js_string, ADD_DEL_FRIEND_REQ)

        try:
            self.sock.sendall(msg)
            if not add:
                self.all_user_list_event.clear()
        except Exception as e:
            messagebox.showerror("Error", "Error: " + str(e))
            return

    def handle_add_friend_res(self, data):
        js_string = json.loads(data)
        if js_string["result"] == "success":
            add = js_string["add"]
            if add:
                user = self.all_users[int(js_string["friend_id"])]
                self.add_user_to_tree(user.user_id, user.user_name, user.online)
                messagebox.showinfo("Success", "Add friend success")
            else:
                messagebox.showerror(
                    "Error", "Add friend failed " + js_string["result"]
                )
        self.all_user_list_event.set()

    def disconnect(self):
        self.clear_treeview()
        self.friend_list.clear()
        self.sock.close()
        self.is_connected = False
        self.is_logged_in = False
        self.stop_event.set()
        self.recv_thread.join()
        print("recv thread join")

    def connect(self, ip, port):
        self.stop_event.clear()
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.connect((ip, port))
        local_info = self.sock.getsockname()  # 获取本地IP和端口
        remote_info = self.sock.getpeername()  # 获取对端IP和端口
        self.connection_string = (
            f"{local_info[0]}:{local_info[1]} -> {remote_info[0]}:{remote_info[1]}"
        )
        self.is_connected = True

    def login(self):
        msg = create_user_login_req(self.user_id, self.pwd)
        self.sock.sendall(msg)

    def start_recv_thread(self):
        self.recv_thread = threading.Thread(target=self.recv_msg_thread_func)
        self.recv_thread.start()

    def handle_disconnect(self):
        self.info_tree.set(self.info_tree_item_id, "connStatCol", "disconnected")
        self.info_tree.item(self.info_tree_item_id, tags=("not_connected",))
        # self.stop_event.set()
        # self.recv_thread.join()
        self.sock.close()
        self.is_connected = False
        self.is_logged_in = False
        self.friend_list.clear()

    def handle_login_res(self, data):
        js_string = json.loads(data)
        if js_string["result"] == "success":
            self.is_logged_in = True
            self.user_name = js_string["name"]
            self.session_id = js_string["session_id"]
            self.tool_tip = f"connection: {self.connection_string}, session_id: {self.session_id}, user_id: {self.user_id}, user_name: {self.user_name}"
            messagebox.showinfo("Info", "Login success")
            self.login_window.destroy()
        else:
            messagebox.showerror("Error", "Login failed, reason:" + js_string["result"])

    def handle_login_success(self):
        pass

    def req_user_list(self, req_all_users=False):
        js_string = json.dumps(
            {
                "id": self.user_id,
                "session_id": int(self.session_id),
                "req_all_users": req_all_users,
            },
            ensure_ascii=False,
        )
        self.all_user_list_event.clear()
        msg = create_msg(js_string, USER_LIST_REQ)
        self.sock.sendall(msg)

    def handle_user_list_res(self, data):
        js_string = json.loads(data)
        req_all = js_string["req_all_users"]
        self.all_users.clear()
        if req_all:
            if js_string["result"] != "success":
                messagebox.showerror(
                    "Error", "Get all user list failed " + js_string["result"]
                )
                self.all_user_list_event.set()
                return
            else:
                for user in js_string["friend_users"]:
                    self.all_users[user["id"]] = friend_info(
                        user["id"], user["name"], user["online"], None
                    )
                self.all_user_list_event.set()
        else:
            if "friend_users" not in js_string:
                return
            for user in js_string["friend_users"]:
                self.add_user_to_tree(user["id"], user["name"], user["online"])

    def add_user_to_tree(self, user_id, user_name, online):
        constatus = "online" if online else "offline"
        if online:
            item_id = self.info_tree.insert(
                "", tk.END, values=(user_name, constatus), tags=("online")
            )
        else:
            item_id = self.info_tree.insert(
                "", tk.END, values=(user_name, constatus), tags=("offline")
            )
        friend_user = friend_info(user_id, user_name, online, item_id)
        friend_user.tool_tip = f"id:{user_id}, name:{user_name}, online:{online}"
        self.friend_list[item_id] = friend_user
        self.friend_id_list[friend_user.user_id] = friend_user

    def handle_user_online_offline(self, data):
        js_string = json.loads(data)
        if js_string["online"]:
            print(js_string["id"])
            self.friend_id_list[js_string["id"]].online = True
            self.info_tree.set(
                self.friend_id_list[js_string["id"]].info_tree_item_id,
                "connStatCol",
                "online",
            )
            self.info_tree.item(
                self.friend_id_list[js_string["id"]].info_tree_item_id, tags=("online",)
            )

        else:
            self.friend_id_list[js_string["id"]].online = False
            self.info_tree.set(
                self.friend_id_list[js_string["id"]].info_tree_item_id,
                "connStatCol",
                "offline",
            )
            self.info_tree.item(
                self.friend_id_list[js_string["id"]].info_tree_item_id,
                tags=("offline",),
            )

    def handle_user_chat_msg_res(self, data):
        js_string = json.loads(data)
        if js_string["result"] == "success":
            user_name = self.friend_id_list[js_string["to_id"]].user_name
            show_ctxt = f"{self.user_name}->{user_name}: time:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
            gui_common.insert_colored_line(self.recv_text_box, show_ctxt, "blue")
            gui_common.insert_colored_line(self.recv_text_box, self.sent_msg, "blue")
        else:
            messagebox.showerror("Error", "send failed, reason:" + js_string["result"])

    def handle_user_chat_msg(self, data):
        js_string = json.loads(data)
        if js_string["msg"] != "":
            user_name = self.friend_id_list[js_string["to_id"]].user_name
            show_ctxt = f"{user_name}->{self.user_name}: time:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
            gui_common.insert_colored_line(self.recv_text_box, show_ctxt, "red")
            gui_common.insert_colored_line(self.recv_text_box, js_string["msg"], "red")

    def recv_msg_thread_func(self):
        # 设置接收超时时间为5秒
        print("start recv thread")
        timeout = 2  # 超时时间，单位为秒
        self.sock.setsockopt(
            socket.SOL_SOCKET, socket.SO_RCVTIMEO, struct.pack("ll", timeout * 1000, 0)
        )
        while not self.stop_event.is_set():
            try:
                header = self.sock.recv(8)
                if not header:
                    self.handle_disconnect()
                    break
                else:
                    id, bodylen = struct.unpack("II", header)
                    reallen = socket.ntohl(bodylen)
                    realId = socket.ntohl(id)
                    result = self.sock.recv(reallen)
                    if not result:
                        self.handle_disconnect()
                        break
                    else:
                        while True:
                            if len(result) == reallen:
                                break
                            else:
                                tmp_result = self.recv(reallen - len(result))
                                if not tmp_result:
                                    self.handle_disconnect()
                                    break
                                else:
                                    result += tmp_result
                        print(result.decode("utf-8"))
                        if realId == USER_LOGIN_RES:
                            self.handle_login_res(result)
                        if realId == USER_LIST_RES:
                            self.handle_user_list_res(result)
                        if realId == USER_ONLINE_OFFLINE:
                            self.handle_user_online_offline(result)
                        if realId == USER_CHAT_MSG:
                            self.handle_user_chat_msg(result)
                        if realId == USER_CHAT_RES:
                            self.handle_user_chat_msg_res(result)
                        if realId == USER_REGISTER_RES:
                            self.handle_register_res(result)
                        if realId == ADD_DEL_FRIEND_RES:
                            self.handle_add_friend_res(result)
            except socket.timeout:
                # print("Timeout occurred")
                pass
            except Exception as e:
                print(f"Error: {e}")
                break
